/**************************************************************/
/*  This file is licensed from, and is a trade secret of:     */
/*                                                            */
/*    Mercury Computer Systems, Incorporated                  */
/*    199 Riverneck Road                                      */
/*    Chelmsford, Massachusetts 01824-2820                    */
/*    United States of America                                */
/*    Telephone + 1 978 256-1300                              */
/*                                                            */
/*  Refer to your Software License Agreement for provisions   */ 
/*  on use, duplication, or disclosure.                       */
/*  US. Government users and prime contractors are also       */
/*  governed by the following Data Rights Notice:             */
/*  Use, duplication or disclosure by the US. Government is   */
/*  subject to the provisions set forth in FAR 52.227-14 for  */
/*  restricted rights or other agency data rights provisions  */
/*  on commercial software and documentation, as applicable.  */
/*                                                            */
/*  This file: (a) was developed at private expense, and no   */
/*  part was developed with government funds, (b) is a        */
/*  trade secret of Mercury Computer Systems, Inc. for the    */
/*  purposes of the Freedom of Information Act, (c) is        */
/*  "commercial computer software" subject to limited         */
/*  utilization as provided in the above noted Software       */
/*  License Agreement and (d) is in all respects data         */
/*  belonging exclusively to Mercury Computer Systems, Inc..  */
/*                                                            */
/**************************************************************/

/******************************************************************/
/* Copyright (c) 1999-2003, Mercury Computer Systems, Inc.,       */
/* Chelmsford MA.,and all third party embedded software sources.  */
/* All rights reserved under the Copyright laws of US. and        */
/* international treaties.                                        */
/*                                                                */
/* Patent Pending                                                 */
/******************************************************************/


//$COPYRIGHT_END


#include <iostream>
#include <math.h>

#include "SceOsMisc.h"
#include "audio_io.h"

/*************************************************************
 * AudioComponent constants.
 *************************************************************/

const int AudioComponent::FRAME_BLK_SIZE = BLOCKSIZE;

// Entrypoints to create the components.
extern "C" {
  AUDIO_EXPORT
  POA_CF::Resource *
  CreateAudioCapture (const char *identifier)
  {
    AudioCapture * audio_capture;

    if (AudioCapture::Create (audio_capture, identifier) != success) {
      return 0;
    }

    return (POA_CF::Resource *) audio_capture;
  }

  AUDIO_EXPORT
  POA_CF::Resource *
  CreateAudioPlayback (const char * identifier)
  {
    AudioPlayback * audio_playback;

    if (AudioPlayback::Create (audio_playback, identifier) != success) {
      return 0;
    }

    return(POA_CF::Resource *) audio_playback;
  }
}

/*
 * ----------------------------------------------------------------------
 * AudioComponent (Base Class).
 * ----------------------------------------------------------------------
 */

AudioComponent::AudioComponent (const char * identifier)
  : m_vfPCMData (0),
    m_threadrunning (false),
    CF__Resource_impl (identifier)
{
  m_props = new audio_properties;
}

AudioComponent::~AudioComponent ()
{
  delete m_vfPCMData;
  delete m_props;
}

status
AudioComponent::Init ()
{
  long i;

  m_fAverageRMS = 0.0;
  m_iOldbits = 0;
  m_fOld_avg_delta = 0.0;

  m_bStop = true;
  m_iLineNumber = 0;

  m_vfPCMData = new float [FRAME_BLK_SIZE];

  /* Loop for each buffer */
  for (i=0; i < NBUFFERS; i++) {
    m_audiohdr[i].dwFlags = 0;
  }

  /* Initialize the recording flag */
  m_recording = false;
  /* Initialize the current buffer */
  m_cbuffer = 0;

  m_cf_props.length (2);
  m_cf_props[0].id = "frequency";
  m_cf_props[1].id = "devid";

  return success;
}

// Generic thread OS signature which calls component specific thread function.
void
AudioComponent::RunWorker (void * param)
{
  AudioComponent * component = (AudioComponent *) param;
  component->Run();
} // End of AudioComponent::RunWorker (void * param)

// Update properties using base class implementation.
void
AudioComponent::UpdateAudioProperties ()
{
  // Default values
  m_props->frequency = 16000L;
  m_props->devid = 0L;
} // End of UpdateAudioProperties()

/*
 * ----------------------------------------------------------------------
 * Audio Capture
 * ----------------------------------------------------------------------
 */

status
AudioCapture::Create (AudioCapture *& handle,
		      const char * identifier)
{
  AudioCapture * Temp = new AudioCapture (identifier);

  if (!Temp) {
    handle = 0;
    return fail;
  }

  if (!Temp->Init()) {
    handle = 0;
    return fail;
  }

  handle = Temp;
  return success;
}

AudioCapture::AudioCapture (const char *identifier) :
  AudioComponent(identifier)
{
  m_pcmout = new PcmOutPort_impl ((short *) &m_audio[0], FRAME_BLK_SIZE);  
}

AudioCapture::~AudioCapture()
{
  delete m_pcmout;
}

CORBA::Object_ptr
AudioCapture::getPort(const CORBA::Char* name)
  throw (CORBA::SystemException,
	 CF::PortSupplier::UnknownPort)
{
  std::string temp(name);

  if (temp != "AudioCaptureOut") {
    throw CF::PortSupplier::UnknownPort();
  }

  return m_pcmout->getPort();
} 

void
AudioCapture::start ()
  throw (CORBA::SystemException,
	 CF::Resource::StartError)
{
  if (m_threadrunning) {
    return;
  }

  m_threadrunning = true;
  m_thread.start (RunWorker, (void *) this);
  CF__Resource_impl::start();
} // End of void AudioCapture::start ()

void
AudioCapture::stop ()
  throw (CORBA::SystemException,
	 CF::Resource::StopError)
{
  m_recording = false;
  m_bStop = true;
  m_threadrunning = false;
  m_thread.detach ();
} // End of void AudioCapture::stop ()

#define WRK_ERROR fail

status
AudioCapture::Init ()
{
  status stat;

#ifdef MCS_VERBOSE
  std::cout << "AudioCapture: Initializing." << std::endl;
#endif

  stat = AudioComponent::Init ();

  if (stat != success) {
    return success;
  }

  // Default values
  m_props->frequency = 16000L;
  m_props->devid = 0L;

  // Get properties from SCA component property settings.
  UpdateAudioProperties();
  return success;
}

// Inner loop method that performs component business logic.
status
AudioCapture::Run ()
{
  int SampleCount, total;
  MMRESULT rc;
  WAVEFORMATEX wfx;
  long i;

#ifdef MCS_VERBOSE
  std::cout << "AudioCapture: Listening." << std::endl;
#endif

  /* Populate the wave format structure */
  wfx.wFormatTag = WAVE_FORMAT_PCM;
  wfx.nChannels = 1;
  wfx.nSamplesPerSec = m_props->frequency;
  wfx.wBitsPerSample = 16;
  wfx.nBlockAlign = (wfx.nChannels * wfx.wBitsPerSample) / 8;
  wfx.nAvgBytesPerSec = (wfx.nSamplesPerSec * wfx.nBlockAlign);
  wfx.cbSize = 0;

  /* Open the input wave device */
  rc = waveInOpen (&m_lphwi, m_props->devid, &wfx, 0, 0, CALLBACK_NULL);

  if (rc != MMSYSERR_NOERROR) {
#ifdef MCS_VERBOSE
    std::cout << "AudioCapture: Opening the audio capture device failed: "
	      << std::hex << rc << std::dec << std::endl;
#endif
    return WRK_ERROR;
  }

  /* Loop for each buffer */
  for (i=0; i < NBUFFERS; i++) {
    /* Populate a wave header structure for the buffer */
    m_audiohdr[i].lpData = (LPSTR)(m_audio[i]);
    m_audiohdr[i].dwBufferLength = sizeof(m_audio[i]);
    m_audiohdr[i].dwFlags = 0;

    /* Prepare the buffer */
    rc = waveInPrepareHeader (m_lphwi, &m_audiohdr[i], sizeof(m_audiohdr[i]));

    if (rc != MMSYSERR_NOERROR) {
#ifdef MCS_VERBOSE
    std::cout << "AudioCapture: Failed to prepare wave buffer header "
	      << i << ": " << std::hex << rc << std::dec << std::endl;
#endif
      return WRK_ERROR;
    }

    /* Add a buffer to the list */
    rc = waveInAddBuffer (m_lphwi, &m_audiohdr[i], sizeof(m_audiohdr[i]));

    if (rc != MMSYSERR_NOERROR) {
#ifdef MCS_VERBOSE
      std::cout << "AudioCapture: Failed to add buffer "
		<< i << ": " << std::hex << rc << std::dec << std::endl;
#endif
      return WRK_ERROR;
    }
  }

  /* Set the recording flag */
  m_bStop = false;
  m_recording = true;

  /* Start recording */
  rc = waveInStart (m_lphwi);
  if (rc != MMSYSERR_NOERROR) {
#ifdef MCS_VERBOSE
    std::cout << "AudioCapture: Failed to start audio recording "
	      << std::hex << rc << std::dec << std::endl;
#endif
    return WRK_ERROR;
  }

  while (!m_bStop) {  
    total = 0;

    while (m_recording || (m_audiohdr[m_cbuffer].dwFlags & WHDR_DONE)) {
      /*
       * Wait for a full buffer.
       *
       * We could do something better than busy waiting here.  E.g.,
       * we could arrange for an CALLBACK_EVENT event in waveInOpen.
       */

      volatile DWORD * flag_ptr = &m_audiohdr[m_cbuffer].dwFlags;

      while (!((*flag_ptr) & WHDR_DONE)) {
	// busy waiting
	SCE::OS::sleep (1);  // Sleep for 1 millisecond
      }

      /*
       * Note that UnprepareHeader must be called before we clear the flags 
       * It also must currently be called before we look at dwBytesRecorded.
       * See Microsoft Knowledge Base Article - 147430.
       */

      rc = waveInUnprepareHeader (m_lphwi, &m_audiohdr[m_cbuffer], 
				  sizeof(m_audiohdr[m_cbuffer]));

      if (rc != MMSYSERR_NOERROR) {
#ifdef MCS_VERBOSE
	std::cout << "AudioCapture: Failed to retrieve audio buffer "
		  << m_cbuffer << ": "
		  << std::hex << rc << std::dec << std::endl;
#endif
	return WRK_ERROR;
      }

      SampleCount = m_audiohdr[m_cbuffer].dwBytesRecorded / 2;

#ifdef MCS_VERBOSE
      std::cout << "AudioCapture: Got " << SampleCount << " samples." << std::endl;
#endif

      m_pcmout->pushData ((short *) m_audio[m_cbuffer], SampleCount);

      /* Resubmit the buffer. */
      m_audiohdr[m_cbuffer].dwFlags = 0;
      rc = waveInPrepareHeader (m_lphwi, &m_audiohdr[m_cbuffer], sizeof (m_audiohdr[m_cbuffer]));

      if (rc != MMSYSERR_NOERROR) {
	return WRK_ERROR;
      }

      rc = waveInAddBuffer (m_lphwi, &m_audiohdr[m_cbuffer], sizeof (m_audiohdr[m_cbuffer]));

      if (rc != MMSYSERR_NOERROR) {
	return WRK_ERROR;
      }

      m_cbuffer = (m_cbuffer + 1) % NBUFFERS;
      total += SampleCount;
    }

#ifdef MCS_VERBOSE
    std::cout << "AudioCapture: Done, total " << total << " samples." << std::endl;
#endif
  }

#ifdef MCS_VERBOSE
  std::cout << "AudioCapture: Stopping." << std::endl;
#endif

  /* Reset the input wave device */
  rc = waveInReset (m_lphwi);

  if (rc != MMSYSERR_NOERROR) {
#ifdef MCS_VERBOSE
    std::cout << "AudioCapture: Failed to reset the audio capture device: "
	      << std::hex << rc << std::dec << std::endl;
#endif
    return WRK_ERROR;
  }

  for (i=0; i < NBUFFERS; i++) {
    /* Unprepare the headers */
    rc = waveInUnprepareHeader (m_lphwi, &m_audiohdr[i], sizeof(m_audiohdr[i]));

    if (rc != MMSYSERR_NOERROR) {
      return WRK_ERROR;
    }
  }

  /* Close the input wave device */
  rc = waveInClose (m_lphwi);

  if (rc != MMSYSERR_NOERROR) {
#ifdef MCS_VERBOSE
    std::cout << "AudioCapture: Failed to close the audio capture device: "
	      << std::hex << rc << std::dec << std::endl;
#endif
    return WRK_ERROR;
  }

  return success;
}

/*
 * ----------------------------------------------------------------------
 * Audio Playback
 * ----------------------------------------------------------------------
 */

status
AudioPlayback::Create (AudioPlayback *& Handle, const char * identifier)
{
  AudioPlayback * Temp = new AudioPlayback (identifier);

  if (!Temp) {
    Handle = 0;
    return fail;
  }

  if (!Temp->Init()) {
    Handle = 0;
    return fail;
  }

  Handle = Temp;
  return success;
}

AudioPlayback::AudioPlayback (const char *identifier)
  :  AudioComponent (identifier)
{
  m_pcmin = new PcmInPort_impl (FRAME_BLK_SIZE);
}

AudioPlayback::~AudioPlayback ()
{
  delete m_pcmin;
}

CORBA::Object_ptr
AudioPlayback::getPort (const CORBA::Char* name)
  throw (CORBA::SystemException,
	 CF::PortSupplier::UnknownPort)
{
  std::string temp (name);

  if (temp != "AudioPlaybackIn") {
    throw CF::PortSupplier::UnknownPort();
  }

  return m_pcmin->getPort();
} 

void
AudioPlayback::start ()
  throw (CORBA::SystemException,
	 CF::Resource::StartError)
{
  if (m_threadrunning) {
    return;
  }

  m_threadrunning = true;
  m_thread.start (RunWorker, (void *) this);
  CF__Resource_impl::start();
} // End of void AudioPlayback::start ()

void
AudioPlayback::stop ()
  throw (CORBA::SystemException,
	 CF::Resource::StopError)
{
  m_bStop = true;   
  m_threadrunning = false;
  m_thread.detach ();
} // End of void AudioPlayback::stop ()

#define WRK_ERROR fail

status
AudioPlayback::Init ()
{
  status stat;

#ifdef MCS_VERBOSE
  std::cout << "AudioPlayback: Initializing." << std::endl;
#endif

  WAVEFORMATEX wfx;
  MMRESULT rc;

  stat = AudioComponent::Init ();

  if (stat != success) {
    return success;
  }

  /*
   * The properties should come from component property settings.
   */

  m_props->frequency = 16000L;
  m_props->devid = 0L;

  // Get properties from SCA component property settings.
  UpdateAudioProperties ();

  /* Populate the wave format structure */
  wfx.wFormatTag = WAVE_FORMAT_PCM;
  wfx.nChannels = 1;
  wfx.nSamplesPerSec = m_props->frequency;
  wfx.wBitsPerSample = 16;
  wfx.nBlockAlign = (wfx.nChannels * wfx.wBitsPerSample) / 8;
  wfx.nAvgBytesPerSec = (wfx.nSamplesPerSec * wfx.nBlockAlign);
  wfx.cbSize = 0;

  /* Open the input wave device */
  rc = waveOutOpen (&m_lphwo, m_props->devid, &wfx, 0, 0, CALLBACK_NULL);

  if (rc != MMSYSERR_NOERROR) {
#ifdef MCS_VERBOSE
    std::cout << "AudioPlayback: Opening the audio output device failed: "
	      << std::hex << rc << std::dec << std::endl;
#endif
    return WRK_ERROR;
  }

  int i;
  for (i=0; i < NBUFFERS; i++) {
    m_audiohdr[i].dwFlags = WHDR_DONE;
  }

  return success;
}

status
AudioPlayback::ConvertToAudio (int length)
{
  MMRESULT rc;

  while ((m_audiohdr[m_cbuffer].dwFlags & WHDR_DONE) == 0) {
    // busy waiting again
    SCE::OS::sleep (1);  // Sleep for 1 millisecond
  }

  memcpy (m_audio[m_cbuffer], m_vlPCMData, length*2);

  /* Prepare a header */
  m_audiohdr[m_cbuffer].lpData = (LPSTR)(m_audio[m_cbuffer]);
  m_audiohdr[m_cbuffer].dwBufferLength = length * 2;

  // Note that UnprepareHeader must be called before we clear the flags.
  rc = waveOutUnprepareHeader (m_lphwo, &m_audiohdr[m_cbuffer], sizeof(m_audiohdr[m_cbuffer]));

  if (rc != MMSYSERR_NOERROR) {
#ifdef MCS_VERBOSE
    std::cout << "AudioPlayback: Failed to retrieve audio buffer "
	      << m_cbuffer << ": "
	      << std::hex << rc << std::dec << std::endl;
#endif
    return WRK_ERROR;
  }

  m_audiohdr[m_cbuffer].dwFlags = 0;

  rc = waveOutPrepareHeader (m_lphwo, &m_audiohdr[m_cbuffer], sizeof(m_audiohdr[m_cbuffer]));

  if (rc != MMSYSERR_NOERROR) {
#ifdef MCS_VERBOSE
    std::cout << "AudioPlayback: Failed to prepare wave buffer header "
	      << m_cbuffer << ": " << std::hex << rc << std::dec << std::endl;
#endif
    return WRK_ERROR;
  }

  rc = waveOutWrite (m_lphwo, &m_audiohdr[m_cbuffer], sizeof(m_audiohdr[m_cbuffer]));

  if (rc != MMSYSERR_NOERROR) {
#ifdef MCS_VERBOSE
    std::cout << "AudioPlayback: Failed to play wave buffer "
	      << m_cbuffer << ": " << std::hex << rc << std::dec << std::endl;
#endif
    return WRK_ERROR;
  }

  /* Set the new cbuffer */
  m_cbuffer = (m_cbuffer + 1) % NBUFFERS;
  return success;
}

// Inner loop method that performs component business logic
status
AudioPlayback::Run ()
{
  int SampleCount, total;
  status stat;

  m_bStop = false;

#ifdef MCS_VERBOSE
  std::cout << "AudioPlayback: Listening." << std::endl;
#endif

  while (!m_bStop) {
    total = 0;

    SampleCount = m_pcmin->GetData (&m_vlPCMData);

    // Timeout if nothing is ready to check for stop command
    if (SampleCount == 0) {// timeout
      continue;
    }

#ifdef MCS_VERBOSE
    std::cout << "AudioPlayback: Received " << SampleCount << " samples." << std::endl;
#endif

    stat = ConvertToAudio (SampleCount);

    m_pcmin->ReleaseDataBuffer();

    if (stat != success) {
      return stat;
    }

    total += SampleCount;
  }

#ifdef MCS_VERBOSE
  std::cout << "AudioPlayback: Done, total " << total << " samples" << std::endl;
#endif

#ifdef MCS_VERBOSE
  std::cout << "AudioPlayback: Stopping." << std::endl;
#endif

  MMRESULT rc;

  long i;

  /* Reset the output wave device */
  rc = waveOutReset (m_lphwo);

  if (rc != MMSYSERR_NOERROR) {
#ifdef MCS_VERBOSE
    std::cout << "AudioPlayback: Failed to reset the audio playback device: "
	      << std::hex << rc << std::dec << std::endl;
#endif
    return WRK_ERROR;
  }

  /* Unprepare the headers */
  for (i=0; i < NBUFFERS; i++) {
    rc = waveOutUnprepareHeader (m_lphwo, &m_audiohdr[i], sizeof(m_audiohdr[i]));

    if (rc != MMSYSERR_NOERROR) {
      return WRK_ERROR;
    }
  }

  return success;
}
